package ele_handler

import (
	"context"
	"fmt"
	"go-yuque/logger"
	"go-yuque/yuque/util"
	"golang.org/x/net/html"
	"math"
	"strings"
	"sync"
)

var (
	_handlerInitFuncs []func() YuqueElementHandler
	_mu               = sync.Mutex{}
)

func init() {
	registerInitFunc(
		// 用匿名函数初始化discardHandler，确保调用setSelf绑定self
		func() YuqueElementHandler {
			h := &discardHandler{}
			// 关键：将self绑定到子类自身（h是*discardHandler，实现了yuqueElementHandler接口）
			// h.BaseHandler.self = h
			h.elements = []string{"html", "meta", "lake", "body", "head"}
			// table 和 tbody 丢弃，需要处理它们里面的元素
			h.elements = append(h.elements, "tbody")
			h.elements = append(h.elements, "colgroup", "col")
			return h
		})

}

func findAttribute(node *html.Node, key string) string {
	if node == nil || node.Attr == nil {
		return ""
	}

	for _, attribute := range node.Attr {
		if attribute.Key == key {
			return attribute.Val
		}
	}

	return ""
}

// 注册处理器实例化函数
func registerInitFunc(handlerInitFunc ...func() YuqueElementHandler) {
	_mu.Lock()
	defer _mu.Unlock()
	_handlerInitFuncs = append(_handlerInitFuncs, handlerInitFunc...)
}

// GetHandlerInitFuncs 获取处理器初始化函数，返回副本避免修改
func GetHandlerInitFuncs() []func() YuqueElementHandler {
	_mu.Lock()
	defer _mu.Unlock()
	// 返回副本
	copied := make([]func() YuqueElementHandler, len(_handlerInitFuncs))
	copy(copied, _handlerInitFuncs)
	return copied
}

// HandleHook 钩子
type HandleHook struct {
	DoAfterHandle func(node *html.Node) error
	DoAfterPost   func(node *html.Node) error
	DoAfterPre    func(node *html.Node) error
}

// StartHandle 外部调用入口函数
func StartHandle(ctx *context.Context, node *html.Node) error {

	if node == nil || ctx == nil {
		return nil
	}

	hook, hookOk := (*ctx).Value("hook").(*HandleHook)
	if hookOk {
		// logger.Debugf("钩子函数存在...")
	}

	handlerP := genSpecifyHandler(node)
	if handlerP == nil {
		if len(node.Data) == 0 {
			node.Data = fmt.Sprintf("nodeType=%d", node.Type)
		}
		(*ctx).Value("bookName")
		logger.Errorf("Toc:[%s],元素:[%s]未匹配到处理器,node:%+v",
			(*ctx).Value("bookName"), node.Data[:int(math.Min(10, float64(len(node.Data))))], *node)
	}

	if handlerP != nil {

		handler := *handlerP

		// 初始化处理器
		handler.init(ctx, node)

		// 先循环每个处理器，进行前置处理
		// 放入调用栈
		handler.pushStack(&handler)
		// 前置处理器
		err := handler.preHandle()

		if err != nil {
			return err
		}

		// 执行钩子函数
		if hook != nil && hook.DoAfterPre != nil {
			err := hook.DoAfterPre(node)
			if err != nil {
				return err
			}
		}

		// 处理器处理
		err = handler.doHandle()
		if err != nil {
			return err
		}

		// 钩子函数
		if hook != nil && hook.DoAfterHandle != nil {
			err := hook.DoAfterHandle(node)
			if err != nil {
				return err
			}
		}

		// 后置处理器
		err = handler.postHandle()
		// 弹出调用栈
		handler.popStack()
		if err != nil {
			return err
		}

	} else {
		// 没有处理器，将整个 HTML 放进去
		var buffer = strings.Builder{}
		err := html.Render(&buffer, node)
		if buffer.Len() > 0 {
			sb, sbOk := (*ctx).Value("result").(**strings.Builder)
			if sbOk {
				(**sb).WriteString(buffer.String())
			}
		} else {
			logger.Errorf("无处理器元素获取HTML报错:%+v", err)
		}
	}

	// 钩子函数
	if hook != nil && hook.DoAfterPost != nil {
		err := hook.DoAfterPost(node)
		if err != nil {
			return err
		}
	}

	return nil
}

func genSpecifyHandler(node *html.Node) *YuqueElementHandler {
	funcs := GetHandlerInitFuncs()
	for _, initFunc := range funcs {
		handler := initFunc()
		if handler.checkNode(node) {
			if len(node.Data) <= 0 {
				node.Data = fmt.Sprintf("nodeType=%d", node.Type)
			}
			logger.Debugf("元素:[%s] 匹配到处理器 %T",
				node.Data[:int(math.Min(10, float64(len(node.Data))))], handler)
			return &handler
		}
	}

	return nil
}

// YuqueElementHandler 接口定义
type YuqueElementHandler interface {
	init(ctx *context.Context, node *html.Node)

	// CheckNode 检查是否是该处理器要处理的元素
	checkNode(node *html.Node) bool

	// StartHandle 处理器入口
	// StartHandle(node *html.Node, hook *HandleHook) error

	// PreHandle 前置处理器
	preHandle() error

	// DoHandle 处理过程中
	doHandle() error

	// PostHandle 后置处理器
	postHandle() error

	// GetCtx 程序上下文
	GetCtx() *context.Context

	// GetTextContext 获取文本内容
	GetTextContext() string

	WriteTextContext(string)

	pushStack(handler *YuqueElementHandler)

	popStack()
}

const DOWNLOAD_TOC_PATH_KEY string = "_DOWNLOAD_TOC_PATH"

// BaseHandler 基类定义
type BaseHandler struct {
	elements        []string                               `name:"匹配要处理的元素"`
	attributes      map[string]string                      `name:"匹配要处理的属性，可为空"`
	node            *html.Node                             `name:"处理中的元素节点"`
	textContentP    **strings.Builder                      `name:"元素处理结果文本内容"`
	ctxMap          map[string]any                         `name:"处理器上下文信息"`
	handlerStack    *util.SliceStack[*YuqueElementHandler] `name:"处理器调用栈"`
	downloadTocPath string                                 `name:"Toc下载路径"`
	// self        YuqueElementHandler
	ctx *context.Context `name:"程序上下文"`
}

// 初始化处理器
func (d *BaseHandler) init(ctx *context.Context, node *html.Node) {
	d.node = node
	sb, sbOk := (*ctx).Value("result").(**strings.Builder)

	// 程序上下文放进处理器中
	d.ctx = ctx

	if sbOk {
		// logger.Debugf("结果对象存在...")
		d.textContentP = sb
	} else {
		sbP := &strings.Builder{}
		d.textContentP = &sbP
		ctxT := context.WithValue(*ctx, "result", d.textContentP)
		*ctx = ctxT
	}

	// 处理器上下文，每个处理器单独拥有，用于包裹形标签的前后置处理器
	ctxMap, ctxMapOk := (*ctx).Value("ctxMap").(map[string]any)
	if ctxMapOk {
		d.ctxMap = ctxMap
	} else {
		d.ctxMap = map[string]any{}
		*ctx = context.WithValue(*ctx, "ctxMap", d.ctxMap)
	}

	// 处理器调用栈，干啥用的忘记了
	handlerStack, handlerStackOk := (*ctx).Value("handlerStack").(*util.SliceStack[*YuqueElementHandler])
	if handlerStackOk {
		d.handlerStack = handlerStack
	}
	// logger.Debugf("ctxMap pointer:%p", ctxMap)

	downloadTocPath, downloadTocPathOk := (*ctx).Value(DOWNLOAD_TOC_PATH_KEY).(string)
	if downloadTocPathOk {
		d.downloadTocPath = downloadTocPath
	}
}

func (d *BaseHandler) checkNode(node *html.Node) bool {
	if d.elements == nil || len(d.elements) <= 0 {
		return false
	}
	if node == nil {
		return false
	}

	if node.Type == html.TextNode {
		return false
	}

	nData := node.Data
	for _, ele := range d.elements {
		if ele == nData {
			// 匹配到元素，判断是否需要匹配属性
			if d.attributes != nil && len(d.attributes) > 0 {
				attributesMap := d.attributes
				for key, val := range attributesMap {
					attrVal := findAttribute(node, key)
					// 匹配处理器时，属性值为空即表示属性值可为任意值
					if val == "" && len(attrVal) > 0 {
						return true
					}
					if val == attrVal {
						return true
					}
				}
				return false
			}
			return true
		}
	}

	return false
}

func (d *BaseHandler) GetCtx() *context.Context {
	return d.ctx
}

func (d *BaseHandler) GetTextContext() string {
	return (*d.textContentP).String()
}

func (d *BaseHandler) WriteTextContext(text string) {

	// 临时 strings.builder
	sbT := strings.Builder{}

	defer func() {
		// 打印临时内容
		logger.Debugf("写入MD文本:%s", sbT.String())
		// 将零宽字符替换为空字符
		textT := strings.ReplaceAll(sbT.String(), "\u200B", "")
		// 将临时内容写入 d.textContentP
		(*d.textContentP).WriteString(textT)
	}()

	wrapBlockquote := (d.ctxMap)["blockquote"]
	if wrapBlockquote != nil {
		if wrapBlockquote.(bool) {
			// 被 blockquote 包裹，如果 textContentP 以 \n 结尾，需要在每行前面加上 >
			if strings.HasSuffix((*d.textContentP).String(), "\n") {
				lines := strings.Split(text, "\n")
				for i, line := range lines {
					if i > 0 {
						sbT.WriteString("\n")
					}
					sbT.WriteString("> ")
					sbT.WriteString(line)
				}
			} else {
				sbT.WriteString(text)
			}
			return
		}
	}

	sbT.WriteString(text)
}

func (d *BaseHandler) preHandle() error {
	// 子类实现
	return nil
}

func (d *BaseHandler) doHandle() error {
	// 子类实现
	return nil
}

func (d *BaseHandler) postHandle() error {
	// 子类实现
	return nil
}

func (d *BaseHandler) pushStack(handler *YuqueElementHandler) {
	d.handlerStack.Push(handler)
}

func (d *BaseHandler) popStack() {
	d.handlerStack.Pop()
}

// ---------------------------- 丢弃元素处理器 ----------------------------

type discardHandler struct {
	BaseHandler
}

func (d *discardHandler) checkNode(node *html.Node) bool {
	check := d.BaseHandler.checkNode(node)
	if check {
		return true
	}

	// documentType 也抛弃
	return node.Type == html.DocumentNode
}

func (d *discardHandler) doHandle() error {
	// DO NOTHING
	logger.Debugf("丢弃元素:%v", d.node.Data)
	return nil
}

// ---------------------------- 丢弃元素处理器 ----------------------------
